import { Vec2 } from "../d2/math/Vec2";
import { IVec2 } from "./Defined";

const PI = 3.141592653589793;

/**
 * 数学工具类
 */
export class MathUtil {
	static readonly PIPI = PI * PI;
	static readonly PI2 = PI * 2;
	static readonly PI_RAD = PI / 180;
	static readonly PI_DEG = 180 / PI;

	static readonly RAD_45 = PI / 4;
	static readonly RAD_90 = PI / 2;
	static readonly RAD_135 = PI * 0.75;
	static readonly RAD_180 = PI;
	static readonly RAD_225 = PI * 1.25;
	static readonly RAD_270 = PI * 1.5;
	static readonly RAD_315 = PI * 1.75;
	static readonly RAD_360 = PI * 2;

	/**
	 * 当值为偶数时返回true
	 * @param val
	 */
	static isEven(val: number) {
		return val % 2 == 0;
	}

	/**
	 * 当值为奇数时返回true
	 * @param val
	 */
	static isOdd(val: number) {
		return val % 2 != 0;
	}

	/**
	 * 将值限制在指定范围内
	 * @param val 值
	 * @param min 最小值
	 * @param max 最大值
	 */
	static clamp(val: number, min: number, max: number): number {
		return val < min ? min : val > max ? max : val;
	}

	/**
	 * 将值限制在0-1范围内
	 * @param val 值
	 */
	static clamp01(val: number): number {
		return val < 0 ? 0 : val > 1 ? 1 : val;
	}

	/**
	 * 将数值限定在一个正循环范围内
	 * @param val 数值
	 * @param len 循环范围 0 ~ len
	 */
	static repeat(val: number, len: number): number {
		const num = Math.floor(val / len);
		return val - num * len;
	}

	/**
	 * 角度转弧度
	 * @param ang 角度
	 */
	static ang2Rad(ang: number) {
		return ang * this.PI_RAD;
	}

	/**
	 * 弧度转角度
	 * @param rad 弧度
	 */
	static rad2Deg(rad: number) {
		return rad * this.PI_DEG;
	}

	/**
	 * 使弧度限定在0 ~ 2pi范围内
	 * @param rad 弧度
	 */
	static normalizeRad(rad: number) {
		return this.repeat(rad, MathUtil.PI2);
	}

	/**
	 * 使角度限定在0 ~ 360范围内
	 * @param deg 角度
	 */
	static normalizeDeg(deg: number) {
		return this.repeat(deg, 360);
	}

	/**
	 * 弧度与目标弧度的差值
	 * @param rad 弧度
	 * @param target 目标弧度
	 */
	static radDiff(rad: number, target: number) {
		let diff = this.normalizeRad(target - rad);
		if (diff > MathUtil.RAD_180) {
			diff -= MathUtil.RAD_360;
		}
		return diff;
	}

	/**
	 * 角度与目标角度的差值
	 * @param deg 角度
	 * @param target 目标角度
	 */
	static degDiff(deg: number, target: number) {
		let diff = this.normalizeDeg(target - deg);
		if (diff > 180) {
			diff -= 360;
		}
		return diff;
	}

	/**
	 * 弧度是否在弧度区间内
	 * @param rad 弧度
	 * @param rad1 弧度左区间
	 * @param rad2 弧度右区间
	 */
	static radBetween(rad: number, rad1: number, rad2: number) {
		rad = this.normalizeRad(rad);
		rad1 = this.normalizeRad(rad1);
		rad2 = this.normalizeRad(rad2);

		if (rad1 <= rad2) {
			return rad >= rad1 && rad <= rad2;
		} else {
			return rad >= rad1 || rad <= rad2;
		}
	}

	/**
	 * 角度是否在角度区间内
	 * @param deg 角度
	 * @param deg1 角度左区间
	 * @param deg2 角度右区间
	 */
	static degBetween(deg: number, deg1: number, deg2: number) {
		deg = this.normalizeDeg(deg);
		deg1 = this.normalizeDeg(deg1);
		deg2 = this.normalizeDeg(deg2);

		if (deg1 <= deg2) {
			return deg >= deg1 && deg <= deg2;
		} else {
			return deg >= deg1 || deg <= deg2;
		}
	}

	/**
	 * 线性插值
	 * @param from
	 * @param to
	 * @param t
	 */
	static lerp(from: number, to: number, t: number) {
		const clampedT = this.clamp01(t);
		return from + (to - from) * clampedT;
	}

	/**
	 * 二阶贝塞尔曲线
	 * @param t
	 * @param p1
	 * @param p2
	 * @param cp
	 * @param out
	 */
	static bezier(t: number, p1: IVec2, p2: IVec2, cp: IVec2, out: IVec2) {
		const x = Math.pow(1 - t, 2) * p1.x + 2 * t * (1 - t) * cp.x + Math.pow(t, 2) * p2.x;
		const y = Math.pow(1 - t, 2) * p1.y + 2 * t * (1 - t) * cp.y + Math.pow(t, 2) * p2.y;
		out.x = x;
		out.y = y;
		return out;
	}
}
